package com.safe.first.interfaces.controller;

import com.safe.first.application.dto.cmd.CreateOrderCmd;
import com.safe.first.application.service.OrderAppService;
import com.safe.first.application.service.RedisDelayQueueAppService;
import com.safe.first.application.service.RedisQueueAppService;
import com.safe.first.common.aspect.WebLog;
import com.safe.first.common.constants.CodeEnum;
import com.safe.first.common.exception.BizException;
import com.safe.first.common.model.R;
import com.safe.first.domain.order.factory.CreateBaseOrderParam;
import com.safe.first.domain.order.factory.GoodsOrderFactory;
import com.safe.first.domain.user.User;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.springframework.web.bind.annotation.*;

import java.security.Principal;
import java.time.Instant;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author Mickey
 * @since 2022/5/22
 **/
@Slf4j
@RestController
@RequestMapping("/order")
@AllArgsConstructor
public class OrderController {

    private final RedissonClient redissonClient;
    private final OrderAppService orderAppService;
    private final RedisQueueAppService redisQueueAppService;
    private final RedisDelayQueueAppService redisDelayQueueAppService;

    public static final String TAKE_ORDER_TOKEN_LUA;

    static {
        TAKE_ORDER_TOKEN_LUA = """
                if (redis.call('exists', KEYS[1]) == 1) then
                    local a = 2
                    local b = 4
                    redis.call('hset', KEYS[1], 'plus', a + b)
                    return '1',
                end
                return ARGV[1]
                """;
    }

    @GetMapping("/test/redis/queue")
    public String testRedisQueue(String data) {
//        testRedisQueue.add(data);
        if (Objects.isNull(data)) {
            throw new BizException(CodeEnum.FAIL);
        }
        return "success";
    }

    @GetMapping("/delay")
    public String delay() {
        RQueue<String> distinationQueue = redissonClient.getQueue("qq");
        RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(distinationQueue);
// 10秒钟以后将消息发送到指定队列
        delayedQueue.offer("msg1", 10, TimeUnit.SECONDS);
// 一分钟以后将消息发送到指定队列
        delayedQueue.offer("msg2", 1, TimeUnit.HOURS);

        return "success";
    }

    @GetMapping("/delay/pro")
    public String delayPro(Integer delay, TimeUnit timeUnit) {
        redisDelayQueueAppService.add(delay, timeUnit);
        return "success";
    }

    @GetMapping("/pop")
    public String pop() {
        RQueue<String> distinationQueue = redissonClient.getQueue("qq");
        RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(distinationQueue);
        delayedQueue.poll();
        return "success";
    }

    @GetMapping("/hello")
    public String hello(Long id) {
//        List<Object> keys = new ArrayList<>();
//        keys.add("safe:match:id:" + id);
//        String eval = redissonClient.getScript().eval(RScript.Mode.READ_WRITE, TAKE_ORDER_TOKEN_LUA, RScript.ReturnType.VALUE, keys, -1);
//        System.out.println(eval);
//        RRateLimiter rateLimiter = redissonClient.getRateLimiter("");
//        rateLimiter.acquire();
        RStream<String, String> stream = redissonClient.getStream("queue");


//        Map<StreamMessageId, Map<String, String>> claim = stream.read(StreamReadArgs.greaterThan(StreamMessageId.AUTO_GENERATED));
//        System.out.println(claim);
        StreamInfo<String, String> info = stream.getInfo();
        List<StreamGroup> streamGroups = stream.listGroups();

        for (StreamGroup streamGroup : streamGroups) {
            String name = streamGroup.getName();
            List<StreamConsumer> streamConsumers = stream.listConsumers(name);
            for (StreamConsumer streamConsumer : streamConsumers) {
                String name1 = streamConsumer.getName();
                System.out.println(name1);
            }
            stream.removeGroup(name);
            System.out.println(name);
        }
        System.out.println(info);
//        stream.createGroup("sensors_data", StreamMessageId.ALL);
//        stream.createConsumer("sensors_data", "ddd");
        return "hello world";
    }

    @GetMapping("/add")
    public String add(Principal principal) {
        System.out.println(principal);
//        RMap<Object, Object> map = redissonClient.getMap("safe:match:id:" + id);
//        map.fastPut("hello", "world");
//        map.fastPut("redis", "map");
//        RStream<String, String> stream = redissonClient.getStream("queue");
//        StreamAddArgs<String, String> entries = StreamAddArgs.entries("speed", "19", "velocity", "39%");
//        StreamTrimStrategyArgs<StreamAddArgs<String, String>> trim = entries.trim();
//        trim.maxLen(10);
//        StreamMessageId add = stream.add(entries);
//        redisQueueAppService.add();

        return "success";
    }

    @PostMapping("/create/base")
    public Object create(@RequestBody CreateOrderCmd<CreateBaseOrderParam> createOrder, User user) {
        return orderAppService.create(createOrder, user);
    }

    @WebLog
    @PostMapping("/create/goods")
    public Object createGoods(@RequestBody CreateOrderCmd<GoodsOrderFactory.CreateGoodsOrderParam> createOrder, User user) {
        return orderAppService.create(createOrder, user);
    }

    @GetMapping("/getTime")
    public R<Instant> getTime() {
        return R.OK(Instant.now());
    }

    @GetMapping("/orderTest")
    public R<String> orderTest() throws InterruptedException {
        byte[] bytes = new byte[1024 * 5];
        Thread.sleep(500);
        return R.OK();
    }

    @GetMapping("/testOrder")
    public R<?> testOrder(User user) {
        System.out.println(user);
        return R.OK();
    }

    @GetMapping("/lock")
    public R<?> lock() {
        RLock test = redissonClient.getLock("test");
        try {
            boolean b = test.tryLock(10L, 10L, TimeUnit.SECONDS);
            System.out.println(b);
            if (b) {
                System.out.println(Thread.currentThread().getName());
                System.out.println(Thread.currentThread().getName() + "进来了");
                Thread.sleep(5000);
            } else {
                System.out.println(Thread.currentThread().getName() + "没抢到");
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            test.unlock();
        }


        return R.OK();
    }

}
